// SPDX-License-Identifier: GPL-2.0

/*
 * csp_ac_n9.c - Common code for LomboTech SoCs
 *
 * Copyright (C) 2016-2018, LomboTech Co.Ltd.
 * Author: lomboswer <lomboswer@lombotech.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/kernel.h>
#include <linux/io.h>
#include <linux/types.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <mach/debug.h>
#include <mach/csp.h>
#include "csp_ac.h"

#undef  MOD_NAME
#define MOD_NAME "N9_AC"

/* set I2S data fmt */
void csp_ac_set_i2sfmt(void *regs, enum i2s_format fmt)
{
}

/* set master/slave mode */
void csp_ac_set_ms_mode(void *regs, enum i2s_ms_mode mode)
{
}

/* init some default config for ac */
void csp_ac_init(void *regs)
{
	csp_ac_poweron(regs);
	/* enable hi pass filter & use left, right input mixer as output */
	writel(0x4, regs + VA_AC_RECCTL - BASE_AC - VA_AC);
	/* set default record master volume to 15db & 0db */
	writel(0x14, regs + VA_AC_VRECL - BASE_AC - VA_AC);
	writel(0x14, regs + VA_AC_VRECR - BASE_AC - VA_AC);
	/* set default record pga volume to 21db & 0db */
#ifdef CONFIG_ARCH_LOMBO_N7V5
	writel(0x3, regs + VA_AC_VPGAL - BASE_AC - VA_AC);
	writel(0x1, regs + VA_AC_VPGAR - BASE_AC - VA_AC);
#else
	writel(0x12, regs + VA_AC_VPGAL - BASE_AC - VA_AC);
	writel(0x4, regs + VA_AC_VPGAR - BASE_AC - VA_AC);
#endif
}

/* power on the ac */
void csp_ac_poweron(void *regs)
{
	reg_ac_pwrm_t pwrm;
	reg_ac_t *reg_ac = regs;

	pwrm.val = READREG32(&reg_ac->pwrm);
	pwrm.bits.pall = 1;
	pwrm.bits.pmbias = 1;
	WRITEREG32(&reg_ac->pwrm, pwrm.val);
}

/* power off the ac */
void csp_ac_poweroff(void *regs)
{
	reg_ac_pwrm_t pwrm;
	reg_ac_t *reg_ac = regs;

	pwrm.val = READREG32(&reg_ac->pwrm);
	pwrm.bits.pall = 0;
	pwrm.bits.pmbias = 0;
	WRITEREG32(&reg_ac->pwrm, pwrm.val);
}

/* mute of unmute the playback */
void csp_ac_playback_mute(void *regs, int mute)
{
	reg_ac_dmuteout_t dmuteout;
	reg_ac_amuteout_t amuteout;
	reg_ac_t *reg_ac = regs;

	dmuteout.val = READREG32(&reg_ac->dmuteout);
	amuteout.val = READREG32(&reg_ac->amuteout);

	if (mute) {
		dmuteout.bits.dmpl = 1;
		amuteout.bits.ampl = 1;
	} else {
		dmuteout.bits.dmpl = 0;
		amuteout.bits.ampl = 0;
	}

	WRITEREG32(&reg_ac->dmuteout, dmuteout.val);
	WRITEREG32(&reg_ac->amuteout, amuteout.val);
}

/* set playback fs */
void csp_ac_set_playback_fs(void *regs, int fs)
{
	reg_ac_accfg_t cfg;
	reg_ac_t *reg_ac = regs;

	cfg.val = READREG32(&reg_ac->accfg);
	switch (fs) {
	case 8000:
		cfg.bits.dacfs = AC_ACCFG_DACFS_0;
		break;
	case 11025:
		cfg.bits.dacfs = AC_ACCFG_DACFS_1;
		break;
	case 12000:
		cfg.bits.dacfs = AC_ACCFG_DACFS_2;
		break;
	case 16000:
		cfg.bits.dacfs = AC_ACCFG_DACFS_3;
		break;
	case 22050:
		cfg.bits.dacfs = AC_ACCFG_DACFS_4;
		break;
	case 24000:
		cfg.bits.dacfs = AC_ACCFG_DACFS_5;
		break;
	case 32000:
		cfg.bits.dacfs = AC_ACCFG_DACFS_6;
		break;
	case 44100:
		cfg.bits.dacfs = AC_ACCFG_DACFS_7;
		break;
	case 48000:
		cfg.bits.dacfs = AC_ACCFG_DACFS_8;
		break;
	case 88200:
		cfg.bits.dacfs = AC_ACCFG_DACFS_9;
		break;
	case 96000:
		cfg.bits.dacfs = AC_ACCFG_DACFS_A;
		break;
	case 192000:
		cfg.bits.dacfs = AC_ACCFG_DACFS_B;
		break;
	default:
		cfg.bits.dacfs = AC_ACCFG_DACFS_7;
		break;
	}

	WRITEREG32(&reg_ac->accfg, cfg.val);
}

/* set record fs */
void csp_ac_set_record_fs(void *regs, int fs)
{
	reg_ac_accfg_t cfg;
	reg_ac_t *reg_ac = (reg_ac_t *)regs;

	cfg.val = READREG32(&reg_ac->accfg);
	switch (fs) {
	case 8000:
		cfg.bits.adcfs = AC_ACCFG_ADCFS_0;
		break;
	case 11025:
		cfg.bits.adcfs = AC_ACCFG_ADCFS_1;
		break;
	case 12000:
		cfg.bits.adcfs = AC_ACCFG_ADCFS_2;
		break;
	case 16000:
		cfg.bits.adcfs = AC_ACCFG_ADCFS_3;
		break;
	case 22050:
		cfg.bits.adcfs = AC_ACCFG_ADCFS_4;
		break;
	case 24000:
		cfg.bits.adcfs = AC_ACCFG_ADCFS_5;
		break;
	case 32000:
		cfg.bits.adcfs = AC_ACCFG_ADCFS_6;
		break;
	case 44100:
		cfg.bits.adcfs = AC_ACCFG_ADCFS_7;
		break;
	case 48000:
		cfg.bits.adcfs = AC_ACCFG_ADCFS_8;
		break;
	case 88200:
		cfg.bits.adcfs = AC_ACCFG_ADCFS_9;
		break;
	case 96000:
		cfg.bits.adcfs = AC_ACCFG_ADCFS_A;
		break;
	case 192000:
		cfg.bits.adcfs = AC_ACCFG_ADCFS_B;
		break;
	default:
		cfg.bits.adcfs = AC_ACCFG_ADCFS_7;
		break;
	}

	WRITEREG32(&reg_ac->accfg, cfg.val);
}

/* set channel cnt */
void csp_ac_set_dac_channel(void *regs, int channel)
{
	reg_ac_accfg_t cfg;
	reg_ac_t *reg_ac = (reg_ac_t *)regs;

	cfg.val = READREG32(&reg_ac->accfg);
	if (channel == 1)
		cfg.bits.dachn = 1;
	else
		cfg.bits.dachn = 0;
	WRITEREG32(&reg_ac->accfg, cfg.val);
}

void csp_ac_set_adc_channel(void *regs, int channel)
{
	reg_ac_accfg_t cfg;
	reg_ac_t *reg_ac = (reg_ac_t *)regs;

	cfg.val = READREG32(&reg_ac->accfg);
	if (channel == 1)
		cfg.bits.adchn = 1;
	else
		cfg.bits.adchn = 0;
	WRITEREG32(&reg_ac->accfg, cfg.val);
}


/* tx control on: 1 start tx, 0 stop tx */
void csp_ac_tx_ctl(void *regs, int on)
{
	reg_ac_accfg_t cfg;
	reg_ac_tfcfg_t tfcfg;
	reg_ac_acrst_t acrst;
	reg_ac_t *reg_ac = (reg_ac_t *)regs;

	tfcfg.val = READREG32(&reg_ac->tfcfg);
	tfcfg.bits.txffr = 1;
	cfg.val = READREG32(&reg_ac->accfg);
	acrst.val = READREG32(&reg_ac->acrst);
	if (on) {
		cfg.bits.ostart = 1;
		acrst.bits.daprst = 1;
		/* reset fifo */
		WRITEREG32(&reg_ac->tfcfg, tfcfg.val);
		/* start tx */
		WRITEREG32(&reg_ac->accfg, cfg.val);
		/* active ac dac path */
		WRITEREG32(&reg_ac->acrst, acrst.val);
	} else {
		cfg.bits.ostart = 0;
		acrst.bits.daprst = 0;
		/* stop tx */
		WRITEREG32(&reg_ac->accfg, cfg.val);
		/* reset fifo */
		WRITEREG32(&reg_ac->tfcfg, tfcfg.val);
		/* reset ac dac path */
		WRITEREG32(&reg_ac->acrst, acrst.val);
	}
}

#ifdef CONFIG_ARCH_LOMBO_N7V5
void csp_ac_pwmdac(void *regs, int on)
{
	reg_ac_anat8_t reg_ac_anat8;
	reg_ac_t *reg_ac = (reg_ac_t *)regs;

	reg_ac_anat8.val = READREG32(&reg_ac->anat8);
	if (on) {
		reg_ac_anat8.bits.pwmo_drv = 0x3;
		reg_ac_anat8.bits.pwmo_pd = 0x0;
		reg_ac_anat8.bits.pwmo_mute = 0x0;
		reg_ac_anat8.bits.pwmo_en = 0x1;
	} else {
		reg_ac_anat8.bits.pwmo_drv = 0x0;
		reg_ac_anat8.bits.pwmo_pd = 0x1;
		reg_ac_anat8.bits.pwmo_mute = 0x1;
		reg_ac_anat8.bits.pwmo_en = 0x0;
	}

	WRITEREG32(&reg_ac->anat8, reg_ac_anat8.val);
}
#endif

/* rx control on: 1 start rx, 0 stop rx */
void csp_ac_rx_ctl(void *regs, int on)
{
	reg_ac_accfg_t cfg;
	reg_ac_rfcfg_t rfcfg;
	reg_ac_pwrin_t pwrin;
	reg_ac_t *reg_ac = (reg_ac_t *)regs;

	rfcfg.val = READREG32(&reg_ac->rfcfg);
	rfcfg.bits.rxffr = 1;
	cfg.val = READREG32(&reg_ac->accfg);
	if (on) {
		cfg.bits.istart = 1;
		/* reset fifo */
		WRITEREG32(&reg_ac->rfcfg, rfcfg.val);
		/* start rx */
		WRITEREG32(&reg_ac->accfg, cfg.val);
	} else {
		cfg.bits.istart = 0;
		/* stop rx */
		WRITEREG32(&reg_ac->accfg, cfg.val);
		/* reset fifo */
		WRITEREG32(&reg_ac->rfcfg, rfcfg.val);
	}

	pwrin.val = READREG32(&reg_ac->pwrin);
	pwrin.bits.padcl = 1; /* padcl */
	pwrin.bits.padcr = 1; /* padcr */
	pwrin.bits.ppgal = 1; /* ppgal */
	pwrin.bits.ppgar = 1; /* ppgar */
	WRITEREG32(&reg_ac->pwrin, pwrin.val);
}

/* set sample bitwidth */
static void csp_ac_set_rxlen(void *regs, u32 rxlen)
{
	reg_ac_rfcfg_t rfcfg;
	reg_ac_t *reg_ac = (reg_ac_t *)regs;

	rfcfg.val = READREG32(&reg_ac->rfcfg);
	switch (rxlen) {
	case 8:
		rfcfg.bits.rxlen = AC_RFCFG_RXLEN_6;
		break;
	case 12:
		rfcfg.bits.rxlen = AC_RFCFG_RXLEN_5;
		break;
	case 16:
		rfcfg.bits.rxlen = AC_RFCFG_RXLEN_4;
		break;
	case 20:
		rfcfg.bits.rxlen = AC_RFCFG_RXLEN_3;
		break;
	case 24:
		rfcfg.bits.rxlen = AC_RFCFG_RXLEN_2;
		break;
	case 28:
		rfcfg.bits.rxlen = AC_RFCFG_RXLEN_1;
		break;
	case 32:
		rfcfg.bits.rxlen = AC_RFCFG_RXLEN_0;
		break;
	default:
		break;
	}

	WRITEREG32(&reg_ac->rfcfg, rfcfg.val);
}

/* set sample bitwidth */
static void csp_ac_set_txlen(void *regs, u32 txlen)
{
	reg_ac_tfcfg_t tfcfg;
	reg_ac_t *reg_ac = (reg_ac_t *)regs;

	tfcfg.val = READREG32(&reg_ac->rfcfg);
	switch (txlen) {
	case 8:
		tfcfg.bits.txlen = AC_TFCFG_TXLEN_6;
		break;
	case 12:
		tfcfg.bits.txlen = AC_TFCFG_TXLEN_5;
		break;
	case 16:
		tfcfg.bits.txlen = AC_TFCFG_TXLEN_4;
		break;
	case 20:
		tfcfg.bits.txlen = AC_TFCFG_TXLEN_3;
		break;
	case 24:
		tfcfg.bits.txlen = AC_TFCFG_TXLEN_2;
		break;
	case 28:
		tfcfg.bits.txlen = AC_TFCFG_TXLEN_1;
		break;
	case 32:
		tfcfg.bits.txlen = AC_TFCFG_TXLEN_0;
		break;
	default:
		break;
	}

	WRITEREG32(&reg_ac->rfcfg, tfcfg.val);
}

void csp_ac_set_bitwidth(void *regs, u32 bitwidth, int dir)
{
	switch (dir) {
	case 0:
		/* playback */
		csp_ac_set_txlen(regs, bitwidth);
		break;
	case 1:
		/* capture */
		csp_ac_set_rxlen(regs, bitwidth);
		break;
	default:
		break;
	}
}
